package cn.bigfire.wzdj;

// 客户端代码

import cn.bigfire.wzdj.frame.WZDJMainFrame;
import cn.bigfire.wzdj.net.WZDJPackWSDecoder;
import cn.bigfire.wzdj.net.WZDJPackWSEncoder;
import cn.bigfire.wzdj.net.WZDJWSClientHandler;
import cn.bigfire.wzdj.pojo.httpVo.*;
import cn.bigfire.wzdj.util.*;
import cn.bigfire.wzdj.util.sign.WZDJAppSign;
import cn.bigfire.wzdj.util.sign.WZDJSign;
import cn.bigfire.wzdj.util.sign.WZDJWXSign;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Console;
import cn.hutool.core.util.StrUtil;
import cn.hutool.cron.CronUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.baidu.bjf.remoting.protobuf.utils.JDKCompilerHelper;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory;
import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler;
import io.netty.handler.codec.http.websocketx.WebSocketVersion;
import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketClientCompressionHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.handler.stream.ChunkedWriteHandler;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import javax.swing.*;
import java.net.URI;
import java.util.Date;

@Slf4j
public class WZDJWSClient implements TcpClient {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            String errStr = "";
            try {
                log.info("JdkCompiler:{}", JDKCompilerHelper.getJdkCompiler().toString());
                //王者对决 Websocket客户端
                WZDJWSClient wzdjClient = new WZDJWSClient();
                //王者对决 游戏窗口
                Stores.setWzdjMainFrame(new WZDJMainFrame(wzdjClient));
                //王者对决 定时任务
                CronUtil.setMatchSecond(true);
                CronUtil.start();
            } catch (Exception e) {
                String throwableStr = throwableStr(e);
                errStr += throwableStr;
                errStr += e.getLocalizedMessage() + "\n";
                log.error(errStr);
                WZDJMainFrame.consoleTextArea.append(errStr);
                JOptionPane.showMessageDialog(null, errStr);
            } catch (Throwable throwable) {
                String throwableStr = throwableStr(throwable);
                errStr += throwableStr;
                errStr += throwable.getLocalizedMessage() + "\n";
                log.error(errStr);
                WZDJMainFrame.consoleTextArea.append(errStr);
                JOptionPane.showMessageDialog(null, errStr);
            }
        });
    }


    public static String throwableStr(Throwable throwable) {
        StringBuilder sb = new StringBuilder();
        StackTraceElement[] stackTrace = throwable.getStackTrace();
        for (StackTraceElement stackTraceElement : stackTrace) {
            final String className = stackTraceElement.getClassName();
            final String methodName = stackTraceElement.getMethodName();
            final String fileName = stackTraceElement.getFileName();
            final Integer lineNumber = stackTraceElement.getLineNumber();
            String format = String.format("%s.%s(%s:%s)%n", className, methodName, fileName, lineNumber);
            sb.append(format);
        }
        return sb.toString();
    }

//    static {
//        System.setProperty("http.proxyHost", "127.0.0.1");
//        System.setProperty("http.proxyPort", "8888"); // Fiddler默认端口
//    }


    @SneakyThrows
    @Override
    public void connect(String ip, int port) {
        Object item = WZDJMainFrame.accountBox.getSelectedItem();
        if (item == null || item.toString() == null || !Stores.localDO.getLoginMap().containsKey(item.toString())) {
            String time = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss");
            log.error("连接失败，请选账号后连接服务器");
            WZDJMainFrame.consoleTextArea.append(time + "连接失败，请选账号后连接服务器" + "\n");
            return;
        }
        //选项 服务器限制 检查
        String selectItemStr = item.toString();//微信账号
        //选项 数据有效性 检查
        WZDJLocalItem wzdjLocalItem = Stores.localDO.getLoginMap().get(selectItemStr);
        if (wzdjLocalItem == null) {//上面检查过，理论上走不到这里
            log.error("错误的选项");
            WZDJMainFrame.consoleTextArea.append("错误的选项" + "\n");
            return;
        }
        //1.缓存 accessToken 登录
        String oldAccessToken = wzdjLocalItem.getHttpLoginResult().getResult().getAccessToken();
        if (StrUtil.isNotBlank(oldAccessToken)) {//如果 accessToken 不为空，先用accessToken进行登录
            Token2GameUser decodeObj = WZDJSign.decodeObj(oldAccessToken);
            log.info("decodeObj:{}", JSON.toJSONString(decodeObj));
            if (decodeObj != null) {
                log.info("1.缓存token,尝试登录");
                AuthRes authRes = AuthUtil.auth(oldAccessToken, decodeObj.getUserId(), decodeObj.getOpenId());
                if (authRes != null && authRes.getUserdInfo() != null) {
                    Stores.getInstance().setUserInfo(authRes.getUserdInfo());
                    Stores.getInstance().setSelectItemStr(selectItemStr);
                    Stores.getInstance().setWzdjLocalItem(wzdjLocalItem);

                    String cut = "]";
                    if (selectItemStr.contains(cut)) {
                        String loginType = StrUtil.subBefore(selectItemStr, cut, false) + cut;
                        String account = StrUtil.subAfter(selectItemStr, cut, false);
                        Stores.getInstance().setAccount(account);
                        Stores.getInstance().setLoginType(loginType);
                    }

                    log.info("1.缓存token,登录成功");
                    wsConnect();//连接服务器
                    return;//登录成功就不会继续往下走了
                }else {
                    log.info("1.缓存 accessToken,登录失败，下一步打算用code或账号密码登录");
                }
            }
        }

        //登录
        if (selectItemStr.endsWith(Consts.LOGIN_TYPE_APP) || selectItemStr.startsWith(Consts.LOGIN_TYPE_APP)) {
            appLogin(selectItemStr);//APP账号登录
        } else {
            wxLogin(selectItemStr);//微信账号登录
        }
        if (Stores.getInstance().getUserInfo() == null) {
            log.info("{}1.游戏token失效，2.账号信息失效,请重新添加账号", Console.where());
            WZDJMainFrame.consoleTextArea.append("1.token实现，2.账号信息失效,请重新添加账号\n");
            return;
        }
        wsConnect();//连接服务器

    }


    @SneakyThrows
    public static void wxLogin(String selectItemStr) {
        if (Stores.getInstance().getUserInfo() != null) {//已经登录了
            return;
        }
        //尝试使用 WX协议模拟登录小程序
        log.info("2.wx协议 尝试直接获取code 尝试登录");
        WZDJLocalItem wzdjLocalItem = Stores.localDO.getLoginMap().get(selectItemStr);
        WxCheckQrScanOkData wxAccountInfo = wzdjLocalItem.getWxAccountInfo();
        WXAcctSectResp acctSectResp = wxAccountInfo.getAcctSectResp();
        //先获取code
        WxGetCodeRes wxGetCodeRes = WxApi.wxLogin(WxApi.WZDJ_APPID, acctSectResp.getUserName());
        if (wxGetCodeRes == null) {
            log.info("2.缓存账号信息 登录失败,请重新扫码登录");
            return;
        }
        //用code登录
        HttpLoginResult httpLoginResult = WZDJWXSign.loginByWxCode(wxGetCodeRes.getCode());
        if (httpLoginResult != null && httpLoginResult.getResult() != null) {
            wzdjLocalItem.setHttpLoginResult(httpLoginResult);
            wzdjLocalItem.setToken2GameUser(WZDJSign.decodeObj(httpLoginResult.getResult().getAccessToken()));

            Stores.localDO.getLoginMap().put(selectItemStr, wzdjLocalItem);//回写内存数据
            String jsonPrettyStr = JSONUtil.toJsonPrettyStr(Stores.localDO);
            FileUtil.writeString(jsonPrettyStr, Stores.localFile, "UTF-8");//回写磁盘
            String accessToken = wzdjLocalItem.getHttpLoginResult().getResult().getAccessToken();
            AuthRes authRes = AuthUtil.auth(accessToken, wzdjLocalItem.getToken2GameUser().getUserId(), wzdjLocalItem.getToken2GameUser().getOpenId());
            if (authRes != null && authRes.getUserdInfo() != null) {
                //登录成功的用户信息存储
                Stores.getInstance().setUserInfo(authRes.getUserdInfo());
                Stores.getInstance().setSelectItemStr(selectItemStr);
                Stores.getInstance().setWzdjLocalItem(wzdjLocalItem);

                Stores.getInstance().setAccount(acctSectResp.getAlias());//微信登录填充账号
                Stores.getInstance().setPassword(wxGetCodeRes.getCode());//微信登录 填充密码
                Stores.getInstance().setLoginType(Consts.LOGIN_TYPE_WX);//微信登录


                log.info("2.缓存账号信息 登录成功");
            } else {
                log.info("2.缓存账号信息 登录失败，下一步 手动登录了");
            }
        } else {
            log.error("2.缓存账号信息 登录失败，下一步 手动登录了");
        }
    }


    /**
     * APP 登录
     */
    public static void appLogin(String selectItemStr) {
        if (Stores.getInstance().getUserInfo() != null) {//已经登录了
            return;
        }
        //尝试使用 账号密码登录
        WZDJLocalItem wzdjLocalItem = Stores.localDO.getLoginMap().get(selectItemStr);
        AppAccountInfo appAccountInfo = wzdjLocalItem.getAppAccountInfo();
        HttpLoginResult httpLoginResult = WZDJAppSign.loginByAccount(appAccountInfo.getAccount(), appAccountInfo.getPassword());
        if (httpLoginResult != null && httpLoginResult.getResult() != null) {
            wzdjLocalItem.setHttpLoginResult(httpLoginResult);
            wzdjLocalItem.setToken2GameUser(WZDJSign.decodeObj(httpLoginResult.getResult().getAccessToken()));

            Stores.localDO.getLoginMap().put(selectItemStr, wzdjLocalItem);//回写内存数据
            String jsonPrettyStr = JSONUtil.toJsonPrettyStr(Stores.localDO);
            FileUtil.writeString(jsonPrettyStr, Stores.localFile, "UTF-8");//回写磁盘
            String accessToken = wzdjLocalItem.getHttpLoginResult().getResult().getAccessToken();
            AuthRes authRes = AuthUtil.auth( accessToken,wzdjLocalItem.getToken2GameUser().getUserId(), wzdjLocalItem.getToken2GameUser().getOpenId());
            if (authRes!=null && authRes.getUserdInfo()!=null){
                //登录成功的用户信息存储
                Stores.getInstance().setUserInfo(authRes.getUserdInfo());
                Stores.getInstance().setSelectItemStr(selectItemStr);
                Stores.getInstance().setWzdjLocalItem(wzdjLocalItem);

                Stores.getInstance().setAccount(appAccountInfo.getAccount());//APP登录
                Stores.getInstance().setPassword(appAccountInfo.getPassword());//APP登录
                Stores.getInstance().setLoginType(Consts.LOGIN_TYPE_APP);//APP登录

                log.info("2.缓存账号信息 登录成功");
            }else {
                log.info("2.缓存账号信息 登录失败，下一步 手动登录了");
            }
        } else {
            log.error("2.缓存账号信息 登录失败，下一步 手动登录了 result:{}", httpLoginResult);
        }
    }

    public static void wsConnect() {
        try {
            String wss = String.format("wss://%s:%s/xcore", Stores.getInstance().getUserInfo().getNetIp(), Stores.getInstance().getUserInfo().getNetPort());
            URI uri = new URI(wss);
            EventLoopGroup group = new NioEventLoopGroup();
            Bootstrap b = new Bootstrap();
            b.group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<Channel>() {
                        @Override
                        public void initChannel(Channel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            if ("wss".equalsIgnoreCase(uri.getScheme())) {
                                SslContext sslCtx = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();
                                pipeline.addLast(sslCtx.newHandler(ch.alloc()));
                            }
                            // 创建HttpRequest，并设置为WebSocket升级的请求
                            FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, wss);
                            // 设置HTTP头
                            HttpHeaders headers = request.headers();
                            headers.add(HttpHeaderNames.HOST, uri.getHost());
                            headers.add(HttpHeaderNames.ORIGIN, "https://wzdj.hnys68.cn:20211");
                            headers.add(HttpHeaderNames.UPGRADE, "websocket");
                            headers.add(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE);
                            headers.add(HttpHeaderNames.USER_AGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 MicroMessenger/7.0.20.1781(0x6700143B) NetType/WIFI MiniProgramEnv/Windows WindowsWechat/WMPF WindowsWechat(0x63090c11)XWEB/11275");
//                            headers.add("Sec-Websocket-Key", "AU9v7EGqfFibu2f4ksyboQ==");
                            pipeline.addLast(new HttpClientCodec());
                            pipeline.addLast(new HttpObjectAggregator(8192));
                            pipeline.addLast(new ChunkedWriteHandler());
                            pipeline.addLast(WebSocketClientCompressionHandler.INSTANCE);
                            pipeline.addLast(new WebSocketClientProtocolHandler(WebSocketClientHandshakerFactory.newHandshaker(uri, WebSocketVersion.V13, "binary", true, headers), true));
                            pipeline.addLast(new WZDJPackWSEncoder());
                            pipeline.addLast(new WZDJPackWSDecoder());
                            pipeline.addLast(new WZDJWSClientHandler());
                        }
                    });
            Channel tmpCchannel = b.connect(uri.getHost(), uri.getPort()).sync().channel();
            Stores.getInstance().setHomeChannel(tmpCchannel);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }












    @SneakyThrows
    @Override
    public void sendMsg(String text) {
        /*
        if (Stores.getInstance().getHomeChannel() != null && text != null) {
            String prefix = "b:";
            if (text.startsWith(prefix)) {
                String after = StrUtil.subAfter(text, prefix, true);
                byte[] bytes = after.getBytes("UTF-8");
                ByteBufAllocator allocator = Stores.getInstance().getHomeChannel().config().getAllocator();
                ByteBuf buffer = allocator.buffer(bytes.length);
                buffer.writeBytes(bytes);
                Stores.getInstance().getHomeChannel().writeAndFlush(new BinaryWebSocketFrame(buffer));
            } else if (text.startsWith("home:")) {
                String after = StrUtil.subAfter(text, "home:", true);
                if ("match".equals(after)) {
                    WZDJSender.rpcServerFightMatch(1);
                }
            } else if (text.startsWith("f:")) {
                String after = StrUtil.subAfter(text, "fight:", true);
                if ("call".equals(after)) {//召唤
                    WZDJSender.rpcServerFightFighterCall();
                }
            } else if ("login".equals(text)) {
                login(Stores.getInstance().getHomeChannel(), Stores.getInstance().getUserInfo().getToken(), Stores.getInstance().getUserInfo().getUuid());
            } else {
                Stores.getInstance().getHomeChannel().writeAndFlush(new TextWebSocketFrame(text));
            }
        }
         */
    }

//    public static void wxLogin(Channel channel, String internaltoken, String uuid) {
////        WZDJPack<Wzdj.RpcServerLogin> wzdjPack = new WZDJPack<>(wzdjHeader, wzdjServerLogin);
//        //body  protobuf中有map类型的protostuff好像不太好处理。先自己创建实体对象解决。后续再详细解决
//        WZDJServerLogin wzdjFirst = new WZDJServerLogin();
//        WZDJLoginInfo wzdjInfo = new WZDJLoginInfo();
//        wzdjInfo.getExtraParams().setKey("wxOpenId");
//        wzdjInfo.getExtraParams().setVal(uuid);
//        wzdjInfo.setInternalToken(internaltoken);
//        wzdjFirst.setInfo(wzdjInfo);
//        WZDJHeader wzdjHeader = WZDJHeader.ofById(655363);
//        WZDJPack<WZDJServerLogin> wzdjPack = new WZDJPack<>(wzdjHeader, wzdjFirst);
//        ByteBuf buffer = Unpooled.buffer();
//        byte[] bytes = wzdjPack.getBytes();
//        log.info("登录数据包大小:" + ByteUtil.getSize(bytes.length));
//        buffer.writeBytes(bytes);
//        BinaryWebSocketFrame binaryWebSocketFrame = new BinaryWebSocketFrame(buffer);
//        channel.writeAndFlush(binaryWebSocketFrame);
//    }


}

